/* Declarations for the array type.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef __KP_ARRAY__
#define __KP_ARRAY__   1

#include "interp.hpp"
#include "initop.hpp"

KP_DECLS_BEGIN

struct alignas (object) array : public varobj
{
  static const uint32_t nonref_flag = 1u << 15;
  static const int code = typecode::ARRAY;

  uint32_t len;
  object *data;

  static array* alloc_raw (uint32_t nelems);

  // Needed by the raw_acc interface.

  uint32_t& len_ref ()
    {
      return (this->len);
    }

  object* data_ptr ()
    {
      return (this->data);
    }

  void local_init (object *data = nullptr, uint32_t len = 0)
    {
      this->data = data;
      this->len = len;
    }

  struct iterator
    {
      valref value;
      object *curr;
      object *last;

      inline iterator (interpreter *interp, object ax, uint32_t i = 0);

      iterator (interpreter *interp, const iterator& right) :
          value (interp, *right.value), curr (right.curr), last (right.curr)
        {
        }

      bool valid () const
        {
          return (this->curr < this->last);
        }

      object operator* () const
        {
          return (*this->value);
        }

      iterator& operator++ ()
        {
          ++this->curr;
          if (this->valid ())
            *this->value = *this->curr;
          return (*this);
        }

      iterator operator++ (int)
        {
          iterator ret { interpreter::self (), *this };
          ++*this;
          return (ret);
        }
    };
};

inline array* as_array (object obj)
{
  return ((array *)unmask (obj));
}

#ifdef KP_ARCH_WIDE

inline constexpr bool array_p (object obj)
{
  return (itype (obj) == typecode::ARRAY);
}

#else

inline bool array_p (object obj)
{
  return (varobj_p (obj) && as_varobj(obj)->vo_type == typecode::ARRAY);
}

#endif

template <class T>
inline object& xaref (object obj, T idx)
{
  return (as_array(obj)->data[idx]);
}

inline uint32_t
len_a (object obj)
{
  return (as_array(obj)->len);
}

array::iterator::iterator (interpreter *interp, object ax, uint32_t i) :
  value (interp, UNBOUND), curr (&xaref(ax, i)), last (&xaref(ax, len_a (ax)))
{
  if (len_a (ax))
    *this->value = *this->curr;
}

struct stream;
struct io_info;
struct pack_info;
struct comparator;

// Allocate an array of length NELEM, filling it with FILL.
KP_EXPORT object alloc_array (interpreter *interp,
  uint32_t nelem, object fill = UNBOUND);

// Add 2 arrays.
KP_EXPORT object add_aa (interpreter *interp, object a1, object a2);

// Concatenate ARGC arrays from ARGV.
KP_EXPORT object concat_a (interpreter *interp, object *argv, int argc);

// Multiply an array by an integer.
KP_EXPORT object mul_ia (interpreter *interp, object ival, object array);

// Index an array.
KP_EXPORT object get_a (interpreter *interp,
  object array, object idx, object dfl);

// Destructively set an object inside an array.
KP_EXPORT object nput_a (interpreter *interp,
  object array, object idx, object val);

// Compute the hashcode of an array.
KP_EXPORT uint32_t hash_a (interpreter *interp, object obj);

// Get the subsequence of an array.
KP_EXPORT object subseq_a (interpreter *interp,
  object array, object i1, object i2);

// Copy an array.
KP_EXPORT object copy_a (interpreter *interp, object obj, bool deep);

// Reverse an array.
KP_EXPORT object reverse_a (interpreter *interp, object obj);

// Destructively reverse an array.
KP_EXPORT object nreverse_a (interpreter *interp, object obj);

// Destructively sort an array.
KP_EXPORT object nsort_a (interpreter *interp, object obj, comparator& c);

// Test for array equality.
KP_EXPORT bool eq_aa (interpreter *interp, object a1, object a2);

// Compare 2 arrays.
KP_EXPORT int cmp_aa (interpreter *interp, object a1, object a2);

// Iterator interface for an array.
KP_EXPORT object iter_a (interpreter *interp,
  object obj, object token, bool adv);

// Mutate an object inside the array
KP_EXPORT object nzap_a (interpreter *interp, object obj, object key,
  uint32_t flags, object fn, object *argv, int argc);

// Return the last element of an array.
KP_EXPORT object last_a (interpreter *interp, object obj);

// Find an element in an array.
KP_EXPORT object find_a (interpreter *interp, object obj,
  object key, object start, object end, object test);

// Write an array to a stream.
KP_EXPORT int write_a (interpreter *interp,
  stream *strm, object obj, io_info& info);

// Serialize an array in a stream.
KP_EXPORT int64_t pack_a (interpreter *interp,
  stream *strm, object obj, pack_info& info);

// Deserialize an array from a stream.
KP_EXPORT object unpack_a (interpreter *interp,
  stream *strm, pack_info& info, bool save);

// Init OP for arrays.
KP_EXPORT init_op init_array;

KP_DECLS_END

#endif
